#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#define FALSE -1  
#define TRUE 0  

#include "rs485_serial.h"

struct struct_rs485_serial  rs485_ser;

static void ser_open(void)
{
  if((rs485_ser.fd = open(rs485_ser.dev, O_RDWR|O_NOCTTY)) < 0 )
  {
    printf("serial fd unopen\n");
    return ;
  }
  
  if(fcntl(rs485_ser.fd, F_SETFL,FNDELAY) < 0)
  {
    printf("fd,F_SETFL,FNDELAY\n");
  }
}

static void ser_close(void)
{
  close(rs485_ser.fd);
}

int speed_arr[] = {
B0, B50, B75, B110,
B134, B150, B200, B300,
B600, B1200, B1800, B2400,
B4800, B9600, B19200, B38400,
B57600, B115200, B230400, B460800,
B500000, B576000, B921600, B1000000,
B1152000, B1500000, B2000000, B2500000,
B3000000, B3500000, B4000000};

int name_arr[] = {
0, 50, 75, 110,
134, 150, 200, 300,
600, 1200, 1800, 2400,
4800, 9600, 19200, 38400,
57600, 115200, 230400, 460800,
500000, 576000, 921600, 1000000,
1152000, 1500000, 2000000, 2500000,
3000000, 3500000, 4000000};

static void set_speed(int speed)
{
  int   i;   
  int   status;   
  struct termios   Opt;
  //printf("speed: %d\n", speed);
  tcgetattr(rs485_ser.fd, &Opt);   
  for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++) 
  {   
    if  (speed == name_arr[i]) 
    { 
      //printf("name_arr[%d]\n",i);
      tcflush(rs485_ser.fd, TCIOFLUSH);       
      cfsetispeed(&Opt, speed_arr[i]);    
      cfsetospeed(&Opt, speed_arr[i]);     
      status = tcsetattr(rs485_ser.fd, TCSANOW, &Opt);    
      if  (status != 0) 
      {          
        perror("tcsetattr rs485_ser.fd1\n");    
        return;       
      }      
      tcflush(rs485_ser.fd,TCIOFLUSH);     
    }    
  }  
  /*
  tcgetattr(rs485_ser.fd, &Opt);
  printf("cfgetispeed: %d\n", cfgetispeed(&Opt));
  printf("cfgetospeed: %d\n", cfgetispeed(&Opt));
  printf("B460800: %d\n",B460800);
  */
}

static int set_parity(int databits,int stopbits,int parity)  
{   
  struct termios options;   
  if  ( tcgetattr( rs485_ser.fd,&options)  !=  0) 
  {   
    perror("SetupSerial 1\n");       
    return(FALSE);    
  }  
  options.c_cflag &= ~CSIZE;   
  switch (databits)   
  {     
  case 7:       
    options.c_cflag |= CS7;   
    break;  
  case 8:       
    options.c_cflag |= CS8;  
    break;     
  default:      
    fprintf(stderr,"Unsupported data size\n"); return (FALSE);    
  }  
  switch (parity)   
  {     
  case 'n':  
  case 'N':      
    options.c_cflag &= ~PARENB;   /* Clear parity enable */  
    options.c_iflag &= ~INPCK;     /* Enable parity checking */   
    break;    
  case 'o':     
  case 'O':       
    options.c_cflag |= (PARODD | PARENB);   
    options.c_iflag |= INPCK;             /* Disnable parity checking */   
    break;    
  case 'e':    
  case 'E':     
    options.c_cflag |= PARENB;     /* Enable parity */      
    options.c_cflag &= ~PARODD;      
    options.c_iflag |= INPCK;       /* Disnable parity checking */  
    break;  
  case 'S':   
  case 's':  /*as no parity*/     
    options.c_cflag &= ~PARENB;  
    options.c_cflag &= ~CSTOPB;break;    
  default:     
    fprintf(stderr,"Unsupported parity\n");      
    return (FALSE);    
  }    
  switch (stopbits)  
  {     
  case 1:      
    options.c_cflag &= ~CSTOPB;    
    break;    
  case 2:      
    options.c_cflag |= CSTOPB;    
    break;  
  default:      
    fprintf(stderr,"Unsupported stop bits\n");    
    return (FALSE);   
  }   
  /* Set input parity option */   
  if (parity != 'n')     
    options.c_iflag |= INPCK;   
  tcflush(rs485_ser.fd,TCIFLUSH);  
  options.c_cc[VTIME] = 150;   
  options.c_cc[VMIN] = 0; /* Update the options and do it NOW */  
  if (tcsetattr(rs485_ser.fd,TCSANOW,&options) != 0)     
  {   
    perror("SetupSerial 3\n");     
    return (FALSE);    
  }   
  return (TRUE);    
}  

static void set_dev(char *dev, int len)
{
  if(len >= 30)
  {
    return ;
  }

  memcpy(rs485_ser.dev, dev, len);
  rs485_ser.dev[len] = '\0';
  //printf("%s\n", rs485_ser.dev);
}
 
static int ser_read(uint8_t *data, int size)
{
  int bytes;
  ioctl(rs485_ser.fd, FIONREAD, &bytes);
  if(bytes == 0)
  {
    return 0;
  }
  ssize_t recv_len = read(rs485_ser.fd, data, size);
  //printf("recv_len: %d\n",recv_len);
  return recv_len;
}

static void ser_write(uint8_t *data, int len)
{
  write(rs485_ser.fd, data, len);
  /*
  int bytes;
  ioctl(rs485_ser.fd, TIOCOUTQ, &bytes);
  printf("bytes: %d\n", bytes);
  */
  rs485_ser.send_len = len;
}

static int  ser_check_write(void)
{
  int bytes;
  ioctl(rs485_ser.fd, TIOCOUTQ, &bytes);
  /*
  printf("bytes: %d\n", bytes);
  printf("send_len: %d\n",rs485_ser.send_len);
  */
  return bytes;
}

void rs485_ser_init(void)
{
  rs485_ser.open = ser_open;
  rs485_ser.close = ser_close;
  rs485_ser.set_parity = set_parity;
  rs485_ser.set_speed = set_speed;
  rs485_ser.set_dev = set_dev;
  rs485_ser.write = ser_write;
  rs485_ser.read = ser_read;
  rs485_ser.check_write = ser_check_write;
}
